package edu.hda.pt.client.connections;

import java.net.InetSocketAddress;

import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.transport.socket.nio.NioSocketConnector;

import edu.hda.pt.client.PTClient;
import edu.hda.pt.client.connections.mina.AsyncClientProtocolHandler;
import edu.hda.pt.client.connections.mina.ClientProtocolHandler;
import edu.hda.pt.client.connections.mina.SyncClientProtocolHandler;
import edu.hda.pt.utils.Stream;
import edu.hda.pt.utils.TestMethod;
import edu.hda.pt.utils.TestObject;
import edu.hda.pt.utils.Stream.StreamState;
import edu.hda.pt.utils.connections.mina.MinaConstants;
import edu.hda.pt.utils.connections.mina.codec.CodecFactory;

/**
 * Client implementation for the connection method Apache MINA
 * 
 * @author Marc Hammerton
 * 
 */
public class PTClientMINA extends PTClient {

	private NioSocketConnector connector;
	private ConnectFuture cf;
	private IoSession session;
	private ClientProtocolHandler handler;

	private String serverIP;

	public Stream stream;

	/**
	 * Constructor for setting up the MINA client. 
	 * 
	 * @param serverIP - The IP address of the MINA server
	 */
	public PTClientMINA(String serverIP) {
		this.serverIP = serverIP;

		connector = new NioSocketConnector();
		connector.getSessionConfig().setTcpNoDelay(true);
		connector.getFilterChain().addLast("protocol",
				new ProtocolCodecFilter(new CodecFactory()));

		// connector.getFilterChain().addLast( "logger", new LoggingFilter() );

		if (PTClient.test2run == TestMethod.STREAMING) {
			handler = new AsyncClientProtocolHandler(this);
		} else {
			handler = new SyncClientProtocolHandler(this);
		}
		connector.setHandler(handler);

		session = null;
	}

	/** 
	 * @see edu.hda.pt.client.PTClient#connect()
	 */
	@Override
	public void connect() {
		cf = connector.connect(new InetSocketAddress(this.serverIP,
				MinaConstants.PORT));

		// Wait for the connection attempt to be finished.
		cf.awaitUninterruptibly(10000);

		while (!cf.isConnected()) {
			System.out.println(this.clientID + " waiting to connect");
			cf.awaitUninterruptibly(10000);
		}
		session = cf.getSession();
	}

	/**
	 * @see edu.hda.pt.client.PTClient#disconnect()
	 */
	@Override
	public void disconnect() {
		if (session != null && session.isConnected()) {
			session.close(true);
		}
		session = null;

		connector.dispose();

	}

	/**
	 * @see edu.hda.pt.client.PTClient#sendBool(boolean)
	 */
	@Override
	protected void sendBool(int messageId, boolean boolPrim) {

		session.write(boolPrim);

		handler.responseReceived();

	}

	/**
	 * @see edu.hda.pt.client.PTClient#sendBytes(byte[])
	 */
	@Override
	protected void sendBytes(int messageId, byte[] bytes) {
		session.write(bytes);
		handler.responseReceived();
	}

	/**
	 * @see edu.hda.pt.client.PTClient#sendInt(int)
	 */
	@Override
	protected void sendInt(int messageId, int intPrim) {
		session.write(intPrim);
		handler.responseReceived();
	}

	/**
	 * @see edu.hda.pt.client.PTClient#sendLong(long)
	 */
	@Override
	protected void sendLong(int messageId, long longPrim) {
		session.write(longPrim);
		handler.responseReceived();
	}

	/**
	 * @see edu.hda.pt.client.PTClient#sendObject(edu.hda.pt.utils.TestObject)
	 */
	@Override
	protected void sendObject(int messageId, TestObject testObject) {
		session.write(testObject);
		handler.responseReceived();
	}

	/**
	 * @see edu.hda.pt.client.PTClient#sendString(java.lang.String)
	 */
	@Override
	protected void sendString(int messageId, String text) {
		session.write(text);
		handler.responseReceived();
	}

	/**
	 * @see edu.hda.pt.client.PTClient#sendStreamingRequest(int, int)
	 */
	@Override
	protected void sendStreamingRequest(int fs) {
		System.out.println(this.clientID + ": requesting stream");
		stream = new Stream(fs);
		stream.setState(StreamState.START);
		session.write(stream);
	}

	/**
	 * @see edu.hda.pt.client.PTClient#terminateStreaming()
	 */
	@Override
	protected void terminateStreaming() {
		if (stream != null && stream.getState() == StreamState.RUNNING) {
			stream.setState(StreamState.FINISH);
			session.write(stream);
			System.out.println(this.clientID + ": stopping stream");
			try {
				Thread.sleep(20000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}
